package com.kevin.netty.starter.util;

import com.kevin.netty.starter.preperties.SslProperties;
import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;

import javax.net.ssl.SSLException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;

@Slf4j
public class SSLContextUtil {

    private static final String[] PROTOCOLS = {"SSLv2Hello", "TLSv1", "TLSv1.1", "TLSv1.2"};

    public static SslContext sslContext(SslProperties sslProperties) {
        final InputStream caCertInput = getResource(sslProperties.getCaCert());
        final InputStream serverCertInput = getResource(sslProperties.getServerCert());
        final InputStream serverPrivateKeyInput = getResource(sslProperties.getServerPrivateKey());
        try {
            return serverSsl(caCertInput, serverCertInput, serverPrivateKeyInput,
                    sslProperties.getSslProvider(), sslProperties.getProtocols(),
                    sslProperties.getClientAuth(), sslProperties.getSessionTimeout());
        } catch (Exception e) {
            log.error("SslContext error : {}", e.getMessage(), e);
            return null;
        }
    }

    public static SslContext serverSsl(InputStream caCert,
                                       InputStream serverCert,
                                       InputStream serverPrivateKey,
                                       SslProvider sslProvider,
                                       String[] protocols,
                                       ClientAuth clientAuth,
                                       long sessionTimeout) throws SSLException {
        return SslContextBuilder.forServer(serverCert, serverPrivateKey).sslProvider(sslProvider)
                .trustManager(caCert).protocols((null != protocols && 0 != protocols.length) ? protocols : PROTOCOLS)
                .clientAuth(clientAuth).sessionTimeout(sessionTimeout).build();
    }

    public static SslContext clientSsl(InputStream caCert,
                                       InputStream clientCert,
                                       InputStream clientPrivateKey,
                                       SslProvider sslProvider,
                                       ClientAuth clientAuth) throws SSLException {
        return SslContextBuilder.forClient()
                .trustManager(caCert)
                .keyManager(clientCert, clientPrivateKey)
                .sslProvider(sslProvider).clientAuth(clientAuth).build();
    }

    public static InputStream getResource(String uri) {
        if (!StringUtils.hasText(uri)) {
            return null;
        }
        if (uri.startsWith("http") || uri.startsWith("https")) {
            try {
                return download(uri);
            } catch (Exception e) {
                log.error("download cert error : {}", e.getMessage(), e);
                return null;
            }
        }
        try {
            Resource resource = new ClassPathResource(uri);
            return resource.getInputStream();
        } catch (Exception e) {
            log.error("load cert error : {}", e.getMessage(), e);
            return null;
        }
    }

    public static InputStream download(String url) throws Exception {
        URL urlStr = new URI(url).toURL();
        URLConnection urlConnection = urlStr.openConnection();
        HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection;
        return httpURLConnection.getInputStream();
    }

}
